/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package busquedaAmplitud;

import gui.ExcepcionParada;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;

/**
 *
 * Clase que implementa la busqueda en Amplitud
 */
public class busquedaAmplitudA {
    public boolean parar =false;
    public List<NodoA> cola; //Lista que almacena los nodos generados
    public NodoA inicial; //nodo inicial
    public NodoA objetivo; //nodo objetivo
    //Variable usada en el proceso a la hora de asignar Id a los nodos sucesores generados
    public int idSgte = 1;
    //Parámetros y flags usados en los metodos de la clase
    public long tiempoTotalBusqueda;
    public int costoFinal;
    public int nivelTope = 0;
    boolean activoNivelTope = false; //Indica si el usuario elegio parar la busqueda en un nivel determinado
    public boolean llegoFinal = false;
    public busquedaAmplitudA(NodoA padre, int nivelTopeR){
        cola = new ArrayList();
        inicial = padre;
        inicial.setNivel(padre.getNivel());
        inicial.setCosto(padre.getCosto());
        inicial.setId(padre.getId());
        inicial.setIdPadre(padre.getIdPadre());
        inicial.mostrarValores();
        //inicializamos parametros
        this.tiempoTotalBusqueda = 0;
        this.costoFinal = 0;
        if(nivelTopeR==0){
            //No se selecciono un nivel tope de parada
       }else{
           this.nivelTope = nivelTopeR;
           this.activoNivelTope = true;
           //La busqueda parara en el nivelTope
       }

    }
    //Metodo que hace efectiva la busqueda
    public void buscarAmplitud() throws ExcepcionParada{
        //Aca comenzamos a contar el tiempo
        long tiempoInicio = System.currentTimeMillis();
        long totalTiempo = 0;
        int cantNivelesRecorridos = 0;
        cola.add(inicial);
        int indicePos = 0;
        boolean esFinal = false;

        //Trabajamos con busqueda hasta el nivel tope
        if(this.activoNivelTope){
            
            while((!esFinal)&&(cantNivelesRecorridos<this.nivelTope)){   
            NodoA actual = cola.get(indicePos);
            actual.setCosto(cola.get(indicePos).getCosto());
            actual.setId(cola.get(indicePos).getId());
            actual.setIdPadre(cola.get(indicePos).getIdPadre());
            actual.setNivel(cola.get(indicePos).getNivel());
            SucesorA tenerHijos = new SucesorA(actual);
            //Aplicamos todas las reglas para obtener los sucesores
            boolean aplico = false;
            aplico = tenerHijos.obtenerHijoRegla1(this.getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla2(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla3(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla4(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla5(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla6(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla7(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla8(getIdSgte());
            this.setIdSgte(this.getIdSgte() + 1);

            List<NodoA> hijos;
            hijos = tenerHijos.getHijos();
            //Por cada hijo obtenido se efectuan los controles antes de agregarlos
            for(int j=0;j<hijos.size();j++){
                NodoA evaluado;
                evaluado = hijos.get(j);
                evaluado.setCosto(hijos.get(j).getCosto());
                evaluado.setId(hijos.get(j).getId());
                evaluado.setIdPadre(hijos.get(j).getIdPadre());
                evaluado.setNivel(hijos.get(j).getNivel());

                if(this.controlarBucle(evaluado)){

                }else{
                    if(this.esEstadoFinal(evaluado)){
                        this.llegoFinal = true;
                        esFinal = true;
                        this.setObjetivo(evaluado);
                        getObjetivo().setCosto(evaluado.getCosto());
                        getObjetivo().setId(evaluado.getId());
                        getObjetivo().setIdPadre(evaluado.getIdPadre());
                        getObjetivo().setNivel(evaluado.getNivel());
                        getObjetivo().mostrarValores();
                    System.out.println("Termino la busqueda");
                    break;
                }
                cola.add(evaluado);
                //Esto hay que hacer para poder mostrar el camino en caso de que se pare la ejecucion por decision del usuario
                this.setObjetivo(evaluado);
                objetivo.setPosicion(evaluado.getPosicion());
                objetivo.setPeso(evaluado.getPeso());
                objetivo.setCosto(evaluado.getCosto());
                objetivo.setId(evaluado.getId());
                objetivo.setIdPadre(evaluado.getIdPadre());
                objetivo.setNivel(evaluado.getNivel());
                
                //Esto esta aca porque si no da errores al mostrar al final cuando hay que buscar hasta un cierto nivel
                if(cantNivelesRecorridos<=this.nivelTope){
                        this.setObjetivo(evaluado);
                        getObjetivo().setCosto(evaluado.getCosto());
                        getObjetivo().setId(evaluado.getId());
                        getObjetivo().setIdPadre(evaluado.getIdPadre());
                        getObjetivo().setNivel(evaluado.getNivel());
                }
                evaluado.mostrarValores();
            }
        }

        indicePos = indicePos + 1;
         
      //Aca recalculamos el tiempo
            totalTiempo = System.currentTimeMillis() - tiempoInicio;
            this.setTiempoTotalBusqueda(totalTiempo);
            if(this.parar){

               esFinal = false;
               JOptionPane.showMessageDialog(null, "La Búsqueda ha sido detenida por decisión del usuario");
               throw new ExcepcionParada("La busqueda ha sido detenida por el usuario");


            }
            //Se resguarda por las dudas
            cantNivelesRecorridos = hijos.get(0).getNivel();
            }
            //Seteamos la bandera
             if(esFinal){
                 this.setLlegoFinal(false);
             }

        }else{

            //Sin conteo de niveles
            while((!esFinal)){
            NodoA actual = cola.get(indicePos);
            actual.setCosto(cola.get(indicePos).getCosto());
            actual.setId(cola.get(indicePos).getId());
            actual.setIdPadre(cola.get(indicePos).getIdPadre());
            actual.setNivel(cola.get(indicePos).getNivel());
            SucesorA tenerHijos = new SucesorA(actual);
            //Aplicamos todas las reglas para obtener los sucesores
            boolean aplico = false;
            aplico = tenerHijos.obtenerHijoRegla1(this.getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla2(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla3(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla4(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla5(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla6(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla7(getIdSgte());
            if(aplico){
                this.setIdSgte(this.getIdSgte() + 1);
            }
            aplico = tenerHijos.obtenerHijoRegla8(getIdSgte());
            this.setIdSgte(this.getIdSgte() + 1);

            List<NodoA> hijos;
            hijos = tenerHijos.getHijos();
            //Por cada hijo obtenido, hacemos los controles antes de agregarlos
            for(int j=0;j<hijos.size();j++){
                NodoA evaluado;
                evaluado = hijos.get(j);
                evaluado.setCosto(hijos.get(j).getCosto());
                evaluado.setId(hijos.get(j).getId());
                evaluado.setIdPadre(hijos.get(j).getIdPadre());
                evaluado.setNivel(hijos.get(j).getNivel());
                if(this.controlarBucle(evaluado)){

                }else{


                    if(this.esEstadoFinal(evaluado)){
                        this.llegoFinal = true;
                        esFinal = true;
                        this.setObjetivo(evaluado);
                        getObjetivo().setCosto(evaluado.getCosto());
                        getObjetivo().setId(evaluado.getId());
                        getObjetivo().setIdPadre(evaluado.getIdPadre());
                        getObjetivo().setNivel(evaluado.getNivel());
                        getObjetivo().mostrarValores();
                        System.out.println("Termino la busqueda");
                        break;
                }
                cola.add(evaluado);
                evaluado.mostrarValores();
                //Esto hay que hacer para poder mostrar el camino en caso de que se pare la ejecucion por decision del usuario
                this.setObjetivo(evaluado);
                objetivo.setPosicion(evaluado.getPosicion());
                objetivo.setPeso(evaluado.getPeso());
                objetivo.setCosto(evaluado.getCosto());
                objetivo.setId(evaluado.getId());
                objetivo.setIdPadre(evaluado.getIdPadre());
                objetivo.setNivel(evaluado.getNivel());
            }
        }
        indicePos = indicePos + 1;
      //Aca recalculamos el tiempo
            totalTiempo = System.currentTimeMillis() - tiempoInicio;
            this.setTiempoTotalBusqueda(totalTiempo);
            if(this.parar){
            esFinal = false;
            JOptionPane.showMessageDialog(null, "La Búsqueda ha sido detenida por decisión del usuario");
            throw new ExcepcionParada("La busqueda ha sido detenida por el usuario");
            }

            }

        }

    }
    //Metodo que implementa el control de bucles al tener hijos, es decir tener hijos repetidos
    public boolean controlarBucle(NodoA recibido){
        boolean esta = false;
        for(int i=0; i<getCola().size();i++){
            NodoA aux;
            aux = getCola().get(i);
            int[][] matrizAux;
            matrizAux = aux.getPosicion();
            int [][] matrizRec;
            matrizRec = recibido.getPosicion();
            //Comparamos elemento a elemento en la matriz
            boolean igual = true;
               for(int p=0;p<8;p++){
                   for(int q=0;q<8;q++){
                      if(matrizAux[p][q] != matrizRec[p][q]){
                        igual = false;
                      }
                   }
               }

        if(igual){
            esta = true;
            break;
        }

        }
        return esta;
    }
    //Metodo que verifica si el nodo recibido como parametro es estado final
    public boolean esEstadoFinal(NodoA recibido){
        boolean es = false;
        int [][] valores;
        valores = new int[8][8];
        valores = recibido.getPosicion();
        int cant1 = 0;
        for(int i=0;i<8;i++){
            for(int j=0;j<8;j++){
                if(valores[i][j]==1){
                    cant1++;
                }
            }
        }

        if(cant1==0){
            es = true;
            //this.costoFinal = recibido.getCosto();
        }
        return es;


    }

    //Devuelve el recorrido desde el estado inicial hasta el final efectuado
    public List<NodoA> camino(){
        System.out.println("Termino la Búsqueda");
        List<NodoA> caminito;
        caminito = new ArrayList();
        caminito.add(getObjetivo());
        int nivel = this.getObjetivo().getNivel()-1 ;
        int idPadre = this.getObjetivo().getIdPadre();
        System.out.println("Nivel objetivo");
        System.out.println(this.getObjetivo().getNivel());
        System.out.println("Id objetivo");
        System.out.println(this.getObjetivo().getId());
        System.out.println("Id padre del objetivo:");
        System.out.println(this.getObjetivo().getIdPadre());

        while(nivel>0){

            for(int i=(cola.size()-1);i>0;i--){

           if((cola.get(i).getId()==idPadre)&&(cola.get(i).getNivel()==nivel)){
                NodoA elm;
                elm = cola.get(i);
                elm.setCosto(cola.get(i).getCosto());
                elm.setId(cola.get(i).getId());
                elm.setIdPadre(cola.get(i).getIdPadre());
                elm.setNivel(cola.get(i).getNivel());
                caminito.add(cola.get(i));
                nivel = nivel - 1;
                idPadre = cola.get(i).getIdPadre();
            }
        }

        }

        caminito.add(inicial);

        this.inicial.mostrarValores();
        for(int j=(caminito.size()-1);j>=0;j--){
            caminito.get(j).mostrarValores();
            System.out.println();
        }
        
        return caminito;
    }

    //Getters y Setters

    public NodoA getObjetivo() {
        return objetivo;
    }

    /**
     * @param objetivo the objetivo to set
     */
    public void setObjetivo(NodoA objetivo) {
        this.objetivo = objetivo;
    }

    /**
     * @return the idSgte
     */
    public int getIdSgte() {
        return idSgte;
    }

    /**
     * @param idSgte the idSgte to set
     */
    public void setIdSgte(int idSgte) {
        this.idSgte = idSgte;
    }

    /**
     * @return the tiempoTotalBusqueda
     */
    public long getTiempoTotalBusqueda() {
        return tiempoTotalBusqueda;
    }

    /**
     * @param tiempoTotalBusqueda the tiempoTotalBusqueda to set
     */
    public void setTiempoTotalBusqueda(long tiempoTotalBusqueda) {
        this.tiempoTotalBusqueda = tiempoTotalBusqueda;
    }

    /**
     * @return the costoFinal
     */
    public int getCostoFinal() {
        return costoFinal;
    }

    /**
     * @param costoFinal the costoFinal to set
     */
    public void setCostoFinal(int costoFinal) {
        this.costoFinal = costoFinal;
    }

    /**
     * @return the nivelTope
     */
    public int getNivelTope() {
        return nivelTope;
    }

    /**
     * @param nivelTope the nivelTope to set
     */
    public void setNivelTope(int nivelTope) {
        this.nivelTope = nivelTope;
    }

    public int cantidadTotalNodosGenerados(){
        return this.cola.size();
    }

     /**
     * @return the cola
     */
    public List<NodoA> getCola() {
        return cola;
    }

    /**
     * @param cola the cola to set
     */
    public void setCola(List<NodoA> cola) {
        this.cola = cola;
    }

    /**
     * @return the inicial
     */
    public NodoA getInicial() {
        return inicial;
    }

    /**
     * @param inicial the inicial to set
     */
    public void setInicial(NodoA inicial) {
        this.inicial = inicial;
    }

    /**
     * @return the llegoFinal
     */
    public boolean isLlegoFinal() {
        return llegoFinal;
    }

    /**
     * @param llegoFinal the llegoFinal to set
     */
    public void setLlegoFinal(boolean llegoFinal) {
        this.llegoFinal = llegoFinal;
    }

    /**
     * @return the parar
     */
    public boolean isParar() {
        return parar;
    }

    /**
     * @param parar the parar to set
     */
    public void setParar(boolean parar) {
        this.parar = parar;
    }
}
